using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;

namespace Deepcode.Flux.Core.UI.Controls
{
	/// <summary>
	/// Maintains a collection of Tab objects
	/// </summary>
	public class TabCollection : IList, IStateManager
	{
		private ArrayList _tabs;				// This will hold the list of Tab objects
		private bool _isTrackingViewState;		// Yes / No : Are we tracking changes to view state
		private bool _saveAll;					// Do we need to save all of our Tab's, or just the changes

		/// <summary>
		/// Constructor :: Create the arraylist object
		/// </summary>
		internal TabCollection()
		{
			_tabs = new ArrayList();
		}

		#region IList Members We've Re-implemented
		/// <summary>
		/// Implement an index method called as in : myobject[x], to 
		/// return the Tab object at the specified index position
		/// </summary>
		public Tab this[int index]
		{
			get
			{
				return (Tab)_tabs[index];
			}
		}
		/// <summary>
		/// Insert a Tab object into the list at the specified location
		/// </summary>
		/// <param name="index"></param>
		/// <param name="item"></param>
		public void Insert(int index, Tab item)
		{
			if (item == null) throw new ArgumentNullException("item");
			_tabs.Insert(index, item);

			// If we know we're supposed to track viewstate, tell the item itself
			// to now start tracking it's own changes.
			if (_isTrackingViewState)
			{
				((IStateManager)item).TrackViewState();
				_saveAll = true; // We've added a new one, so we'll have to save all of the viewstates again
			}
		}
		/// <summary>
		/// Remove the Tab specified from the list
		/// </summary>
		/// <param name="item"></param>
		public void Remove(Tab item)
		{
			if (item == null) throw new ArgumentNullException("item");
			int index = IndexOf(item);
			if (index >= 0) RemoveAt(index);
		}
		/// <summary>
		/// Checks to see if this collection contains the Tab specified
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool Contains(Tab item)
		{
			if (item == null) return false;
			return _tabs.Contains(item);
		}
		/// <summary>
		/// Returns the index position of the Tab object specified
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int IndexOf(Tab item)
		{
			if (item == null) throw new ArgumentNullException("item");
			return _tabs.IndexOf(item);
		}
		/// <summary>
		/// Adds a Tab object to the list
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int Add(Tab item)
		{
			if (item == null) throw new ArgumentNullException("item");
			_tabs.Add(item);

			// If we're supposed to be tracking view state, tell the tab to do the same
			if (_isTrackingViewState)
			{
				((IStateManager)item).TrackViewState();
				item.SetDirty();
			}

			return _tabs.Count - 1;
		}
		#endregion
		#region IList Members we've implemented as per the interface definition
		/// <summary>
		/// Always return false to this property
		/// </summary>
		public bool IsReadOnly { get { return false; } }

		/// <summary>
		/// Original index method, which returns a plain "object"
		/// </summary>
		object IList.this[int index]
		{
			get
			{
				return _tabs[index];
			}
			set
			{
				_tabs[index] = (Tab)value;
			}
		}
		/// <summary>
		/// Remove the object at the index position specified
		/// </summary>
		/// <param name="index"></param>
		public void RemoveAt(int index)
		{
			_tabs.RemoveAt(index);
			// We're tracking view state changes, but because we've removed one, we're going to 
			// have to re-save every tab back into viewstate as their index positions will have
			// now changed.
			if (_isTrackingViewState) _saveAll = true;
		}
		/// <summary>
		/// Insert the specified object into the list. This will ensure that
		/// the object is of the correct type first as we're only storing Tab's
		/// </summary>
		/// <param name="index"></param>
		/// <param name="item"></param>
		void IList.Insert(int index, object item)
		{
			if (item == null) throw new ArgumentNullException("item");
			if (!(item is Tab)) throw new ArgumentException("item must be a Tab");

			Insert(index, (Tab)item);
		}
		/// <summary>
		/// Remove the object specified - but this version will first ensure the type
		/// is of the correct type - Tab.
		/// </summary>
		/// <param name="item"></param>
		void IList.Remove(object item)
		{
			if (item == null) throw new ArgumentNullException("item");
			if (!(item is Tab)) throw new ArgumentException("item must be a Tab");
			Remove((Tab)item);
		}
		/// <summary>
		/// Checks to see if this list contains the object passed in
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		bool IList.Contains(object item)
		{
			return Contains(item as Tab);
		}
		/// <summary>
		/// Clear down the whole list - force a complete
		/// re-save into viewstate
		/// </summary>
		public void Clear()
		{
			_tabs.Clear();
			if (_isTrackingViewState) _saveAll = true;
		}
		/// <summary>
		/// Return the index position of the object specified. The object
		/// must be (and will be checked to ensure it is) of type Tab.
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		int IList.IndexOf(object item)
		{
			if (item == null) throw new ArgumentNullException("item");
			if (!(item is Tab)) throw new ArgumentException("item must be a tab");
			return IndexOf((Tab)item);
		}
		/// <summary>
		/// Add the object specified into the list - this implementation
		/// will first ensure the object is of the appropriate Tab type;
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		int IList.Add(object item)
		{
			if (item == null) throw new ArgumentNullException("item");
			if (!(item is Tab)) throw new ArgumentException("item must be a tab");
			return Add((Tab)item);
		}
		/// <summary>
		/// Return whether or not this collection is of a fixed size. In this
		/// case, the collection can expand and grow or shrink, so it will
		/// always return false.
		/// </summary>
		public bool IsFixedSize { get { return false; } }
		#endregion
		#region ICollection Members
		/// <summary>
		/// </summary>
		public bool IsSynchronized { get { return false; } }

		/// <summary>
		/// Return the number of elements in this collection...
		/// </summary>
		public int Count { get { return _tabs.Count; } }

		/// <summary>
		/// Copy this list into another array
		/// </summary>
		/// <param name="array"></param>
		/// <param name="index"></param>
		public void CopyTo(Array array, int index)
		{
			_tabs.CopyTo(array, index);
		}

		/// <summary>
		/// </summary>
		public object SyncRoot { get { return this; } }
		#endregion
		#region IEnumerable Members
		/// <summary>
		/// Return the enumerator for this tabs collection
		/// </summary>
		/// <returns></returns>
		public IEnumerator GetEnumerator()
		{
			return _tabs.GetEnumerator();
		}

		#endregion
		#region IStateManager Members
		/// <summary>
		/// Force ourselves to start tracking changes to the properties stored
		/// in the viewstate. In this case we delegate the call down to the individual
		/// elements in our list...
		/// </summary>
		public void TrackViewState()
		{
			_isTrackingViewState = true;
			foreach (Tab tab in _tabs)
			{
				((IStateManager)tab).TrackViewState();
			}
		}
		/// <summary>
		/// Return whether or not we're tracking the view state...
		/// </summary>
		public bool IsTrackingViewState { get { return _isTrackingViewState; } }
		/// <summary>
		/// Save the viewstate (or just the changes to the viewstate)
		/// </summary>
		/// <returns></returns>
		public object SaveViewState()
		{
			if (_saveAll == true)
			{
				// We're saving all of the tabs this time...
				ArrayList states = new ArrayList(Count);
				// Loop over each tab and tell the object that it's viewstate
				// is in fact dirty and it needs to save it's viewstate
				// then call the save viewstate function on that object which
				// in turn will return the object of information about this 
				// particular tab and add it into the serialisable list which 
				// this function returns to the caller.
				for (int i = 0; i < Count; i++)
				{
					Tab tab = (Tab)_tabs[i];
					tab.SetDirty();
					states.Add(((IStateManager)tab).SaveViewState());
				}
				if (states.Count > 0) return states;
				else return null;
			}
			else
			{
				// Save only items that are dirty
				ArrayList states = new ArrayList();
				ArrayList indices = new ArrayList();

				for (int i = 0; i < Count; i++)
				{
					Tab tab = (Tab)_tabs[i];
					// Get the viewstate for this tab. Will return null if the viewstate hasn't changed.
					object state = ((IStateManager)tab).SaveViewState();
					if (state != null)
					{
						states.Add(state);	// Add the new state to the viewstate serialisable list
						indices.Add(i);		// Add the index number of the tab to the viewstate serialisable list
					}
				}
				// Return a Pair object for serialisation into viewstate
				if (indices.Count > 0) return new Pair(indices, states);
			}
			return null;
		}
		/// <summary>
		/// Using the object passed in, check it's type and reload the properties
		/// specified....
		/// </summary>
		/// <param name="state"></param>
		public void LoadViewState(object state)
		{
			if (state == null) return;

			if (state is Pair)
			{
				// Only changed items were saved
				Pair p = (Pair)state;
				ArrayList indices = (ArrayList)p.First;
				ArrayList states = (ArrayList)p.Second;

				// Loop over the indices
				for (int i = 0; i < indices.Count; i++)
				{
					// Get the tab's index number
					int index = (int)indices[i];
					if (index < this.Count)
					{
						// If this index is already in our collection, tell it to load viewstate
						((IStateManager)_tabs[index]).LoadViewState(states[i]);
					}
					else
					{
						// If this index isn't in our collection yet, add a new object
						Tab tab = new Tab();
						Add(tab);
						((IStateManager)tab).LoadViewState(states[i]);
					}

				}
			}
			else
			{
				// All items were saved - create a new collection using view state.
				_saveAll = true;
				ArrayList states = (ArrayList)state;
				int count = states.Count;
				_tabs = new ArrayList(count);

				for (int i = 0; i < count; i++)
				{
					Tab t = new Tab();
					Add(t);
					((IStateManager)t).LoadViewState(states[i]);
				}
			}
		}
		#endregion
	}
}
